//@author A0087191Y
package net.mysoc.w111j.service;

import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import javafx.scene.chart.PieChart;
import javafx.scene.chart.XYChart;
import net.mysoc.w111j.model.Category;
import net.mysoc.w111j.model.Expense;
import net.mysoc.w111j.model.Money;

/**
 * A service to perform report-generation functions.
 */
public class ReportService {
    public static Date getStartDateForCurrentPeriod(ReportType type) {
        Calendar cal = Calendar.getInstance();
        switch (type) {
            case YEARLY:
                cal.set(Calendar.MONTH, Calendar.JANUARY);
                cal.set(Calendar.DAY_OF_MONTH, 1);
                cal.set(Calendar.HOUR, 0);
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);
                break;
            case MONTHLY:
                cal.set(Calendar.DAY_OF_MONTH, 1);
                cal.set(Calendar.HOUR, 0);
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);
                break;
            case WEEKLY:
                cal.set(Calendar.DAY_OF_WEEK, cal.getFirstDayOfWeek());
                cal.set(Calendar.HOUR, 0);
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);
                break;
            case DAILY:
                cal.set(Calendar.HOUR, 0);
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);
                break;
        }
        return cal.getTime();
    }

    public static Date getEndDateForPeriod(Date startDate, ReportType type) {
        Date endDate = getDateOffsetByPeriod(startDate, type, 1);

        Calendar cal = Calendar.getInstance();
        cal.setTime(endDate);
        cal.add(Calendar.MILLISECOND, -1);
        endDate = cal.getTime();

        return endDate;
    }

    public static Date getDateOffsetByPeriod(Date startDate, ReportType type, int offset) {
        Calendar cal = Calendar.getInstance();
        cal.setTime(startDate);

        switch (type) {
            case DAILY:
                cal.add(Calendar.DATE, offset);
                cal.set(Calendar.HOUR, 0);
                cal.set(Calendar.HOUR_OF_DAY, 0);
                cal.set(Calendar.MINUTE, 0);
                cal.set(Calendar.SECOND, 0);
                cal.set(Calendar.MILLISECOND, 0);
                break;
            case WEEKLY:
                cal.add(Calendar.WEEK_OF_YEAR, offset);
                break;
            case MONTHLY:
                cal.add(Calendar.MONTH, offset);
                break;
            case YEARLY:
                cal.add(Calendar.YEAR, offset);
                break;
        }

        return cal.getTime();
    }

    public static Date getToday() {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.HOUR, 0);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        return cal.getTime();
    }

    public Report generateReport(ReportType type, List<Expense> expenses) {
        Calendar cal = Calendar.getInstance();
        List<PieChart.Data> pieChartData = new LinkedList<>();
        List<XYChart.Series<Number, Number>> scatterChartData;

        Map<Category, List<Expense>> expensesByCategory = splitExpensesByCategory(expenses);

        scatterChartData = new LinkedList<>();
        for (Category cat : expensesByCategory.keySet()) {
            XYChart.Series<Number, Number> series = new XYChart.Series<>();
            series.setName(cat.getName());

            for (Expense e : expensesByCategory.get(cat)) {
                cal.setTime(e.getDate());
                int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
                series.getData().add(new XYChart.Data<Number, Number>(dayOfWeek, e.getPrice().toBigDecimal(), e));
            }

            scatterChartData.add(series);
        }

        {
            // Calculate totals per category
            BigDecimal total = BigDecimal.ZERO;
            Map<Category, Money> categoryTotals = new HashMap<>();

            for (Expense e : expenses) {
                if (!categoryTotals.containsKey(e.getCategory())) {
                    categoryTotals.put(e.getCategory(), e.getPrice());
                } else {
                    categoryTotals.put(e.getCategory(), e.getPrice().add(categoryTotals.get(e.getCategory())));
                }
                total = total.add(e.getPrice().toBigDecimal());
            }

            for (Map.Entry<Category, Money> entry : categoryTotals.entrySet()) {
                String categoryName = entry.getKey().getName();
                double amount = entry.getValue().toBigDecimal().doubleValue();
                double value = amount / total.doubleValue() * 100;
                pieChartData.add(new PieChart.Data(categoryName, value));
            }

        }

        return new Report(expenses, type, scatterChartData, pieChartData);
    }

    private Map<Category, List<Expense>> splitExpensesByCategory(List<Expense> expenses) {
        Map<Category, List<Expense>> map = new HashMap<>();

        for (Expense e : expenses) {
            Category category = e.getCategory();
            if (!map.containsKey(category)) {
                map.put(category, new LinkedList<Expense>());
            }
            map.get(category).add(e);
        }

        return map;
    }
}
